#include "PipelineStatesManager.h"

#include <cassert>
#include <D3D11.h>

#include "main/D3DData.h"
#include "main/Globals.h"
#include "utils/DxErrorChecker.h"

PipelineStates::PipelineStates()
	: mTransparentBS(transparentBlendState())
	, mLinearSS(linearClampSamplerState())
	, mWireframeRS(wireframeRasterizerState())
	, mDisableDepthState(disableDepthTestDepthStencilState())
{

}

PipelineStates::~PipelineStates() {
    mTransparentBS->Release();
	mLinearSS->Release();
    mWireframeRS->Release();
	mDisableDepthState->Release();
}

ID3D11BlendState*
PipelineStates::transparentBlendState() const {
	D3D11_BLEND_DESC transparentDesc;
	ZeroMemory(&transparentDesc, sizeof(D3D11_BLEND_DESC));
	transparentDesc.AlphaToCoverageEnable = true;
	transparentDesc.IndependentBlendEnable = false;

	transparentDesc.RenderTarget[0].BlendEnable = true;
	transparentDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
	transparentDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
	transparentDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
	transparentDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
	transparentDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
	transparentDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
	transparentDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;

	assert(Globals::gD3D->mDevice);
	ID3D11Device& device = *Globals::gD3D->mDevice;
	ID3D11BlendState* blendState;
	const HRESULT result = device.CreateBlendState(&transparentDesc, &blendState);
	DxErrorChecker(result);
	assert(blendState);

	return blendState;
}

ID3D11SamplerState*
PipelineStates::linearClampSamplerState() const {
	D3D11_SAMPLER_DESC samplerDesc;
	samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
	samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
	samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
	samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
	samplerDesc.MipLODBias = 0;
	samplerDesc.MaxAnisotropy = 0;
	samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	samplerDesc.BorderColor[0] = 0.0f;
	samplerDesc.BorderColor[1] = 0.0f;
	samplerDesc.BorderColor[2] = 0.0f;
	samplerDesc.BorderColor[3] = 0.0f;
	samplerDesc.MinLOD = 0.0f;
	samplerDesc.MaxLOD = 0.0f;

	assert(Globals::gD3D->mDevice);
	ID3D11Device& device = *Globals::gD3D->mDevice;
	ID3D11SamplerState* samplerState;
	const HRESULT result = device.CreateSamplerState(&samplerDesc, &samplerState);
	DxErrorChecker(result);
	assert(samplerState);

	return samplerState;
}

ID3D11RasterizerState* 
PipelineStates::wireframeRasterizerState() const {
	D3D11_RASTERIZER_DESC wireframeDesc;
	ZeroMemory(&wireframeDesc, sizeof(D3D11_RASTERIZER_DESC));
	wireframeDesc.FillMode = D3D11_FILL_WIREFRAME;
	wireframeDesc.CullMode = D3D11_CULL_BACK;
	wireframeDesc.FrontCounterClockwise = false;
	wireframeDesc.DepthClipEnable = true;

	assert(Globals::gD3D->mDevice);
	ID3D11Device& device = *Globals::gD3D->mDevice;
	ID3D11RasterizerState* rasterizerState;
	const HRESULT result = device.CreateRasterizerState(&wireframeDesc, &rasterizerState);
	DxErrorChecker(result);
	assert(rasterizerState);

	return rasterizerState;
}

ID3D11DepthStencilState* 
PipelineStates::disableDepthTestDepthStencilState() const{
	// Disable depth test
	D3D11_DEPTH_STENCIL_DESC dsDesc;
	ZeroMemory(&dsDesc, sizeof(D3D11_DEPTH_STENCIL_DESC));

	// Depth test parameters
	dsDesc.DepthEnable = false;

	// Stencil test parameters
	dsDesc.StencilEnable = false;

	// Create depth stencil state
	assert(Globals::gD3D->mDevice);
	ID3D11Device& device = *Globals::gD3D->mDevice;
	ID3D11DepthStencilState* depthStencilState;
	device.CreateDepthStencilState(&dsDesc, &depthStencilState);
	assert(depthStencilState);

	return depthStencilState;
}